#include "x11utils.h"
#include <iostream>

X11Utils::X11Utils()
{

}

/// @brief get display of x11
/// @return display
Display *X11Utils::disp() {
    if (!xDisplay) {
        xDisplay = XOpenDisplay(NULL);
    }

    return xDisplay;
}

void X11Utils::cleanUpDisplay() {
    if (!xDisplay) {
        return;
    }

    XCloseDisplay(xDisplay);
    xDisplay = nullptr;
}

/// @brief test 
/// @return true or false
bool X11Utils::ewmhIsSupported() {
    Display *display = disp();

    Atom netSupportingWmCheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", true);
    Atom actualType;
    int format = 0;
    unsigned long num = 0, bytes = 0;
    unsigned char *data = nullptr;
    Window ewmh_window = 0;

    int status = XGetWindowProperty(display, DefaultRootWindow(display), netSupportingWmCheck, 0L, 1L, false,
                                    XA_WINDOW, &actualType, &format, &num, &bytes, &data);
    if (status == Success) {
        if (num > 0) {
            ewmh_window = ((Window *)data)[0];
        }
        if (data) {
            XFree(data);
            data = nullptr;
        }
    }

    if (ewmh_window) {
        status = XGetWindowProperty(display, ewmh_window, netSupportingWmCheck, 0L, 1L, false,
                                    XA_WINDOW, &actualType, &format, &num, &bytes, &data);
        if (status != Success || num == 0 || ewmh_window != ((Window *)data)[0]) {
            ewmh_window = 0;
        }

        if (status == Success && data) {
            XFree(data);
        }
    }

    return ewmh_window != 0;
}

/**
 * get all active windows list
 * 
**/
QList<Window> X11Utils::getTopLevelWindows() {
    QList<Window> res;

    if (!ewmhIsSupported()) {
        std::cout << "unable to find window list." << std::endl;

        return res;
    }

    Atom netClList = XInternAtom(disp(), "_NET_CLIENT_LIST", true);
    Atom actualType;
    int format;
    unsigned long num, bytes;
    Window *data = nullptr;

    for (int i = 0; i < ScreenCount(disp()); i++) {
        Window rootWin = RootWindow(disp(), i);

        int status = XGetWindowProperty(disp(), rootWin, netClList, 0L, ~0L, false,
                                        AnyPropertyType, &actualType, &format, &num,
                                        &bytes, (uint8_t **)&data);

        if (status != Success) {
            std::cout << "unable to getting root window properties" << std::endl;
            continue;
        }

        for (unsigned long i = 0; i < num; i++) {
            res.append(data[i]);
        }

        XFree(data);
    }
    
    return res;
}

QString X11Utils::getWindowAtom(Window win, const char *atom) {
    Atom netWmName = XInternAtom(disp(), atom, false);
    int n;
    char **list = 0;
    XTextProperty tp;
    std::string res = "unknown";

    XGetTextProperty(disp(), win, &tp, netWmName);

    if (!tp.nitems) {
        XGetWMName(disp(), win, &tp);
        return "errors";
    }

    if (tp.encoding == XA_STRING) {
        res = (char *)tp.value;
    } else {
        int ret = XmbTextPropertyToTextList(disp(), &tp, &list, &n);
        
        if (ret >= Success && n > 0 && *list) {
            res = *list;
            XFreeStringList(list);
        }
    }

    XFree(tp.value);

    return QString::fromStdString(res);
}

QString X11Utils::getWindowName(Window win) {
    return getWindowAtom(win, "_NET_WM_NAME");
}
